﻿namespace MonetDbDataProvider.Query
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Text.RegularExpressions;

    using MonetDbDataProvider.Mapi;

    public class QueryResult
    {
        // Response should look like this:
        // 0: Header information. Id, TupleCount, ColumnCount, RowCount
        // 1: TableName for each column
        // 2: ColumnNames
        // 3: DataType
        // 4: Data Length
        // 5: Start of actual data.
        public QueryResult(IList<string> response)
        {
            var headerInfo = response[0].Split(' ');
            this.Id = int.Parse(Convert.ToString(headerInfo[1]));
            this.TupleCount = int.Parse(Convert.ToString(headerInfo[2]));
            
            if (this.TupleCount > 0)
                this.Data = GenerateDataTable(response);
        }

        public int Id { get; set; }

        public int TupleCount { get; set; }

        public DataTable Data { get; set; }

        private static DataTable GenerateDataTable(IList<string> response)
        {
            return BuildDataTableData(response, BuildDataTableSchema(response));
        }

        private static DataTable BuildDataTableSchema(IList<string> response)
        {
            var newDataTable = new DataTable();
            var columns = ExtractResponseValue(response[2], '%', '#');
            var dataTypes = ExtractResponseValue(response[3], '%', '#');

            for (var i = 0; i < columns.Length; i++)
            {
                var column = new DataColumn(columns[i].Trim().TrimEnd(','), Utility.GetType(dataTypes[i]));

                newDataTable.Columns.Add(column);
            }

            return newDataTable;
        }

        private static DataTable BuildDataTableData(IList<string> response, DataTable dataTable)
        {
            for (var i = 5; i < response.Count; i++)
            {
                var values = ExtractResponseValue(response[i], '[', ']');
                var dataRow = dataTable.NewRow();

                for (var j = 0; j < values.Length - 1; j++)
                    dataRow[j] = CastValue(values[j].TrimEnd(',').Trim(), dataTable.Columns[j].DataType);

                dataTable.Rows.Add(dataRow);
            }

            return dataTable;
        }

        private static string[] ExtractResponseValue(string values, char startChar, char endChar)
        {
            var start = values.IndexOf(startChar) + 1;
            var end = values.LastIndexOf(endChar);
            var substringLength = values.Length - start - (values.Length - end);

            return values.Substring(start, substringLength).Split('\t');
        }

        private static object CastValue(string value, Type valueType)
        {
            if (valueType == typeof(string) && UnescapeValue(value.TrimStart('"').TrimEnd('"')).ToUpper() == "NULL")
                return DBNull.Value;

            if (valueType == typeof(string)) return Regex.Unescape(value.TrimStart('"').TrimEnd('"'));

            if (valueType == typeof(int)) return int.Parse(value);

            if (valueType == typeof(double)) return double.Parse(value);

            if (valueType == typeof(DateTime)) return DateTime.Parse(value);

            return value;
        }

        private static string UnescapeValue(string value)
        {
            return value.Replace("\'", "'");
        }
    }
}
